package com.pod5.skype.master;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.nio.charset.*;

import com.pod5.skype.master.CrawledIPState;
import com.pod5.skype.master.DataTypeException;


public class NonBlockingServer
{
	private LinkedList<String> rawIPData;
	private LinkedList<String> readableIP;
	private LinkedList<String> crawledIP;
	private CrawledIPState stateIP;
	private ManageIP manager;
	
	public SaveThread st = null;
	
    public Selector sel = null;
    public ServerSocketChannel server = null;
    public SocketChannel socket = null;
    public int port = 5050;


    public NonBlockingServer()
    {
		System.out.println("Inside default ctor");
    }
    
	public NonBlockingServer(int port)
    {
		System.out.println("Inside the other ctor");
		port = port;
    }
	
	private void loadStateInfo()
	{
		if(rawIPData == null)
		{
	    	try{
	  			 rawIPData = stateIP.loadState("rawIPData");
	  			 readableIP = stateIP.loadState("readableIP");
	  			 crawledIP = stateIP.loadState("crawledIP");
	  	 	}
	  	 	catch(DataTypeException e){
	  	 		
	  	 	}
		}
	}

    public void initializeOperations() throws IOException,UnknownHostException
    {
    	stateIP = new CrawledIPState();
    	manager = new ManageIP();
   	 	saveState();
   	 	
		System.out.println("Inside initialization");
		sel = Selector.open();
		server = ServerSocketChannel.open();
		server.configureBlocking(false);
		InetAddress ia = InetAddress.getLocalHost();
		InetSocketAddress isa = new InetSocketAddress(ia,port);
		server.socket().bind(isa);
    }
    
	public void startServer() throws IOException
    {
		System.out.println("Inside startserver");
        initializeOperations();
		System.out.println("Abt to block on select()");
		SelectionKey acceptKey = server.register(sel, SelectionKey.OP_ACCEPT );	
	
		while (acceptKey.selector().select() > 0 )
		{	
	    
			Set readyKeys = sel.selectedKeys();
			Iterator it = readyKeys.iterator();
			loadStateInfo();

			while (it.hasNext()) {
				SelectionKey key = (SelectionKey)it.next();
				it.remove();
                
				if (key.isAcceptable()) {
//					System.out.println("Key is Acceptable");
					ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
					socket = (SocketChannel) ssc.accept();
					socket.configureBlocking(false);
					SelectionKey another = socket.register(sel,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
				}
				if (key.isReadable()) {
//					System.out.println("Key is readable");
					String ret = readMessage(key);
					if(!ret.isEmpty())
					{
						LinkedList<String> newRawIP = parseMessage(ret);
						ret = manager.getNextIP(rawIPData, newRawIP, crawledIP, readableIP);
						writeMessage(socket,ret);
					}
				}
				if (key.isWritable()) {
//					System.out.println("THe key is writable");
					String ret = readMessage(key);
					if(!ret.isEmpty())
					{
						LinkedList<String> newRawIP = parseMessage(ret);
						ret = manager.getNextIP(rawIPData, newRawIP, crawledIP, readableIP);
						socket = (SocketChannel)key.channel();
						writeMessage(socket,ret);
					}
				}
			}
		}
    }

			

    private LinkedList<String> parseMessage(String ret) {
		// TODO Auto-generated method stub
    	
    	LinkedList<String> newRawIP = new LinkedList<String>();
    	if(!ret.contains("Ready") || !ret.contains("quit"))
    	{
    		String[] splitAddr = ret.split(":");
    		
    		for(int i =0; i < (splitAddr.length-1); i++)
    		{
    			newRawIP.add(splitAddr[i]);
    		}
    	}
    	return newRawIP;
	}

	public void writeMessage(SocketChannel socket,String ret)
    {
		System.out.println("Inside the loop");

		try
		{
			ByteBuffer buffer = ByteBuffer.allocate(1024);

				buffer = ByteBuffer.wrap(ret.getBytes());
				int nBytes = socket.write(buffer);
				
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}

    }
  
    public String readMessage(SelectionKey key)
    {
		socket = (SocketChannel)key.channel();
		ByteBuffer buf = ByteBuffer.allocate(1024);
        String result = (String) key.attachment();
        if(result == null)
        	result = "";
		try
		{
            while(socket.read(buf)>0)
            {
				buf.flip();
				Charset charset = Charset.forName("us-ascii");
				CharsetDecoder decoder = charset.newDecoder();
				CharBuffer charBuffer = decoder.decode(buf);
				result += charBuffer.toString();
				buf.clear();
            }
        
        }
		catch(IOException e)
		{
			e.printStackTrace();
		}
		if(result.contains("EOF"))
		{
			key.attach(null);
			return result;
		}
		else
		{
			key.attach(result);
			return "";
		}
    }

    public static void main(String args[])
    {
		NonBlockingServer nb = new NonBlockingServer();
		try
		{
			nb.startServer();
		}
		catch (IOException e)
		{
			e.printStackTrace();
			System.exit(-1);
		}
		
	}
    
    public void saveState()
    {
		st = new SaveThread("Save Thread");
		st.start();

    }

    public void interruptThread()
    {
		st.val = false;
    }
    
    public class SaveThread extends Thread
    {
		public SocketChannel sc = null;
		public boolean val = true;
	
		public SaveThread(String str)
		{
			super(str);
		}
	
		public void run() {

			System.out.println("Inside Saving");
			while (val)
			{
				try {
					if(rawIPData != null)
					{
						stateIP.saveState(rawIPData, "rawIPData");
						stateIP.saveState(readableIP, "readableIP");
						stateIP.saveState(crawledIP, "crawledIP");
					}
					Thread.sleep(1000*60); //Only save once a minute, since we will be running Skype once every 5 mins
				} catch (DataTypeException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
			}
            

		}
    }
}



